﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace Gmantis.Controls
{
    public class WrapPanel : Panel
    {
        // Fields
        public static readonly DependencyProperty BreakLineProperty = DependencyProperty.RegisterAttached("BreakLine", typeof(BreakLine), typeof(WrapPanel), new PropertyMetadata(new PropertyChangedCallback(WrapPanel.OnBreakLinePropertyChanged)));
        public static readonly DependencyProperty ItemHeightProperty = DependencyProperty.Register("ItemHeight", typeof(double), typeof(WrapPanel), new PropertyMetadata((double)1.0 / (double)0.0, new PropertyChangedCallback(WrapPanel.OnItemHeightPropertyChanged)));
        public static readonly DependencyProperty ItemWidthProperty = DependencyProperty.Register("ItemWidth", typeof(double), typeof(WrapPanel), new PropertyMetadata((double)1.0 / (double)0.0, new PropertyChangedCallback(WrapPanel.OnItemWidthPropertyChanged)));
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(WrapPanel), new PropertyMetadata(Orientation.Horizontal, new PropertyChangedCallback(WrapPanel.OnOrientationPropertyChanged)));

        // Methods
        public WrapPanel()
        {
            this.SetCustomDefaultValues();
        }

        private void ArrangeLine(double v, double lineV, int start, int end, bool useItemU, double itemU)
        {
            double num = 0.0;
            bool flag = this.Orientation == Orientation.Horizontal;
            UIElementCollection children = base.Children;
            for (int i = start; i < end; i++)
            {
                UIElement element = children[i];
                if (element != null)
                {
                    UVSize size = new UVSize(this.Orientation, element.DesiredSize.Width, element.DesiredSize.Height);
                    double num3 = useItemU ? itemU : size.U;
                    element.Arrange(new Rect(flag ? num : v, flag ? v : num, flag ? num3 : lineV, flag ? lineV : num3));
                    num += num3;
                }
            }
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            int start = 0;
            double itemWidth = this.ItemWidth;
            double itemHeight = this.ItemHeight;
            double v = 0.0;
            double itemU = (this.Orientation == Orientation.Horizontal) ? itemWidth : itemHeight;
            UVSize size = new UVSize(this.Orientation);
            UVSize size2 = new UVSize(this.Orientation, finalSize.Width, finalSize.Height);
            bool flag = !double.IsNaN(itemWidth);
            bool flag2 = !double.IsNaN(itemHeight);
            bool useItemU = (this.Orientation == Orientation.Horizontal) ? flag : flag2;
            bool flag4 = false;
            int end = 0;
            foreach (UIElement element in base.Children)
            {
                if (element != null)
                {
                    UVSize size3 = new UVSize(this.Orientation, flag ? itemWidth : element.DesiredSize.Width, flag2 ? itemHeight : element.DesiredSize.Height);
                    if ((((size.U + size3.U) > size2.U) || flag4) || ((((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.Before) || (((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.AfterAndBefore)))
                    {
                        this.ArrangeLine(v, size.V, start, end, useItemU, itemU);
                        v += size.V;
                        size = size3;
                        if (size3.U > size2.U)
                        {
                            this.ArrangeLine(v, size3.V, end, ++end, useItemU, itemU);
                            v += size3.V;
                            size = new UVSize(this.Orientation);
                        }
                        start = end;
                    }
                    else
                    {
                        size.U += size3.U;
                        size.V = Math.Max(size3.V, size.V);
                    }
                    flag4 = (((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.After) || (((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.AfterAndBefore);
                }
                end++;
            }
            if (start < base.Children.Count)
            {
                this.ArrangeLine(v, size.V, start, base.Children.Count, useItemU, itemU);
            }
            return finalSize;
        }

        public static BreakLine GetBreakLine(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (BreakLine)element.GetValue(BreakLineProperty);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            UVSize size = new UVSize(this.Orientation);
            UVSize size2 = new UVSize(this.Orientation);
            UVSize size3 = new UVSize(this.Orientation, constraint.Width, constraint.Height);
            double itemWidth = this.ItemWidth;
            double itemHeight = this.ItemHeight;
            bool flag = !double.IsNaN(itemWidth);
            bool flag2 = !double.IsNaN(itemHeight);
            Size availableSize = new Size(flag ? itemWidth : constraint.Width, flag2 ? itemHeight : constraint.Height);
            bool flag3 = false;

            foreach (UIElement element in base.Children)
            {
                if (element != null)
                {
                    element.Measure(availableSize);
                    UVSize size5 = new UVSize(this.Orientation, flag ? itemWidth : element.DesiredSize.Width, flag2 ? itemHeight : element.DesiredSize.Height);
                    if ((((size.U + size5.U) > size3.U) || flag3) || ((((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.Before) || 
                        (((BreakLine)element.GetValue(BreakLineProperty)) == BreakLine.AfterAndBefore)))
                    {
                        size2.U = Math.Max(size.U, size2.U);
                        size2.V += size.V;
                        size = size5;
                        if (size5.U > size3.U)
                        {
                            size2.U = Math.Max(size5.U, size2.U);
                            size2.V += size5.V;
                            size = new UVSize(this.Orientation);
                        }
                    }
                    else
                    {
                        size.U += size5.U;
                        size.V = Math.Max(size5.V, size.V);
                    }
                    flag3 =  (((BreakLine) element.GetValue(BreakLineProperty)) == BreakLine.After) || (((BreakLine) element.GetValue(BreakLineProperty)) == BreakLine.AfterAndBefore);
                }
            }

            size2.U = Math.Max(size.U, size2.U);
            size2.V += size.V;
            return new Size(size2.Width, size2.Height);
        }

        private static void OnBreakLinePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement reference = d as FrameworkElement;
            if (reference != null)
            {
                WrapPanel parent = VisualTreeHelper.GetParent(reference) as WrapPanel;
                if ((parent != null) && !object.Equals(e.OldValue, e.NewValue))
                {
                    parent.InvalidateMeasure();
                }
            }
        }

        private static void OnItemHeightPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WrapPanel panel = d as WrapPanel;
            double oldValue = (double)e.OldValue;
            double newValue = (double)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        private static void OnItemWidthPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WrapPanel panel = d as WrapPanel;
            double oldValue = (double)e.OldValue;
            double newValue = (double)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        private static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WrapPanel panel = d as WrapPanel;
            Orientation oldValue = (Orientation)e.OldValue;
            Orientation newValue = (Orientation)e.NewValue;
            if (newValue != oldValue)
            {
                panel.InvalidateMeasure();
            }
        }

        public static void SetBreakLine(DependencyObject element, BreakLine value)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            element.SetValue(BreakLineProperty, value);
        }

        private void SetCustomDefaultValues()
        {
        }

        // Properties
        public double ItemHeight
        {
            get { return (double)GetValue(ItemHeightProperty); }
            set { SetValue(ItemHeightProperty, value); }
        }

        public double ItemWidth
        {
            get { return (double)GetValue(ItemWidthProperty); }
            set { SetValue(ItemWidthProperty, value); }
        }

        public Orientation Orientation
        {
            get { return (Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }

        // Nested Types
        [StructLayout(LayoutKind.Sequential)]
        private struct UVSize
        {
            internal double U;
            internal double V;
            private Orientation _orientation;
            internal UVSize(Orientation orientation)
            {
                this.U = this.V = 0.0;
                this._orientation = orientation;
            }

            internal UVSize(Orientation orientation, double width, double height)
            {
                this.U = this.V = 0.0;
                this._orientation = orientation;
                this.Width = width;
                this.Height = height;
            }

            internal double Width
            {
                get
                {
                    if (this._orientation == Orientation.Horizontal)
                        return this.U;

                    return this.V;
                }
                set
                {
                    if (this._orientation == Orientation.Horizontal)
                        this.U = value;
                    else
                        this.V = value;
                }
            }

            internal double Height
            {
                get
                {
                    if (this._orientation == Orientation.Horizontal)
                        return this.V;

                    return this.U;
                }
                set
                {
                    if (this._orientation == Orientation.Horizontal)
                        this.V = value;
                    else
                        this.U = value;
                }
            }
        }
    }

    public enum BreakLine : byte
    {
        After = 1,
        AfterAndBefore = 3,
        Before = 2,
        None = 0
    }
}
